<?php

/**
 * @title Shipping Flat
 * @am_plugin_api 6.0
 */
class Am_Plugin_ShippingFlat extends Am_Plugin
{
    const PLUGIN_STATUS = self::STATUS_BETA;
    const PLUGIN_COMM = self::COMM_COMMERCIAL;
    const PLUGIN_REVISION = '6.3.32';

    protected $_configPrefix = 'misc.';

    public function _initSetupForm(Am_Form_Setup $form)
    {
        $form->addText('cost', ['size' => 5])
            ->setLabel("Default Shipping Cost\n" .
                "this amount will be used if invoice has " .
                "at least one tangible item and none of " .
                "the country/address specific rates apply");

        $form->addAdvCheckbox('per_item_charge')
            ->setLabel(___("Cost Is Per Tangible Item?\n".
                "If ticked, the shipping cost will be applied " .
                "as a flat charge to each tangible item, rather " .
                "than as a flat charge per invoice"));

        $gr = $form->addGroup()->setLabel("Free Shipping threshold\n"
                . "make shipping free if total of invoice equal or more than specified value");

        $gr->setSeparator(' ');
        $gr->addAdvCheckbox('free_shipping');
        $gr->addText('free_shipping_threshold', ['size' => 5]);

        $form->addScript()
            ->setScript(<<<CUT
jQuery(function(){
    jQuery('[type=checkbox][name$=free_shipping]').change(function(){
        jQuery(this).nextAll().toggle(this.checked);
    }).change();
})
CUT
);

        $form->addElement(new Am_Form_Element_ShippingRate('shipping_cost'));
    }

    function onGridProductInitForm(Am_Event_Grid $e)
    {
        $e->getGrid()->getForm()
            ->getAdditionalFieldSet()
            ->addAdvCheckbox('is_tangible')
            ->setLabel('Is Tangible?');
    }

    public function onGridProductInitGrid(Am_Event_Grid $e)
    {
        $e->getGrid()->addField('is_tangible', 'Is Tangible?');
        $e->getGrid()->actionAdd(new Am_Grid_Action_LiveCheckbox('is_tangible'));
    }

    function onInvoiceGetCalculators(Am_Event_InvoiceGetCalculators $e)
    {
        $e->replace(new Am_Invoice_Calc_ShippingFlat($this), $e->findByClassName('Am_Invoice_Calc_Shipping'));
    }

    function getCost(User $user = null)
    {
        return $this->_getCost($user) ?: $this->getConfig('cost');
    }

    protected function _getCost(User $user = null)
    {
        if (!$user) return null;
        $cost = null;
        foreach ((array)$this->getConfig('shipping_cost') as $t){
            if (!empty($t['zip']))
                if (!$this->compareZip($t['zip'], $user->zip))
                    continue; // no match
            if (!empty($t['state']) &&
                $t['state'] == $user->state &&
                $t['country'] == $user->country)
            {
                $cost = $t['cost'];
                break;
            }
            if (!$t['state'] &&
                !empty($t['country']) &&
                $t['country'] == $user->get('country'))
            {
                $cost = $t['cost'];
                break;
            }
        }
        return $cost;
    }

    protected function compareZip($zipString, $zip)
    {
        $zip = trim($zip);
        foreach (preg_split('/[,;\s]+/', $zipString) as $s)
        {
            $s = trim($s);
            if (!strlen($s)) continue;
            if (strpos($s, '-')) {
                [$range1, $range2] = explode('-', $s);
            } else {
                $range1 = $range2 = $s;
            }
            if ((strtoupper($range1) <= strtoupper($zip)) && (strtoupper($zip) <= strtoupper($range2)))
            {
                return true;
            }
        }
        return false;
    }
}

class Am_Form_Element_ShippingRate extends HTML_QuickForm2_Element
{
    protected $shipping_rates = [];

    public function getRawValue()
    {
        return $this->shipping_rates;
    }

    protected function updateValue()
    {
        $name = $this->getName();
        $name2 = Am_Form_Setup::name2underscore($name);
        foreach ($this->getDataSources() as $ds) {
            if (null !== ($value = $ds->getValue($name))) {
                $this->setValue($value);
                return;
            }
            if (null !== ($value = $ds->getValue($name2))) {
                $this->setValue($value);
                return;
            }
        }
    }

    public function setValue($value)
    {
        foreach ((array)$value as $k => $r)
        {
            if (is_array($r)) continue;
            $r = preg_replace('/[^A-Za-z0-9_.,|-]/', '', $r);
            $r = explode('|', $r, 4);
            $value[$k] = [
                'country' => $r[0],
                'state'   => $r[1],
                'zip'     => $r[2],
                'cost'    => sprintf('%.2f', $r[3]),
            ];
        }
        $this->shipping_rates = $value;
    }

    public function getType()
    {
        return 'custom_row';
    }

    public function __toString()
    {
        $output = sprintf('<div style="padding:0.5em"><h1>%s</h1><div class="am-grid-container"><table id="shipping_rates-table" %s><tr><th>%s</th>
	<th>%s</th>
        <th>%s</th>
	<th>%s</th>
	<th>&nbsp;</th></tr>',
            ___('Configured Shipping Rates Values'),
            'class="am-grid"',
            ___('Country'),
            ___('State'),
            ___('Zip'),
            ___('Shipping Cost')
        );

        $currency = (string)Am_Currency::getDefault();
        $label_add = ___('Add');
        $output .= "<tr class='shipping_rates-add am-grid-row'>" .
            "<td><select id='shipping_rates-country' size='1' style='max-width:150px'><option/>".Am_Html::renderOptions(Am_Di::getInstance()->countryTable->getOptions())."</select>" .
            "<td><select id='shipping_rates-state' size='1' style='max-width:150px'></select></td>" .
            "<td><input type='text' id='shipping_rates-zip' /></td>" .
            "<td><input type='text' id='shipping_rates-cost' size='5' maxlength='6' /></td>" .
            "<td><input type='button' id='shipping_rates-add-button' value='$label_add'/></td>" .
            "</tr>\n";

        $name = $this->getName();
        foreach ($this->shipping_rates as $id => $region) {
            $hidden = Am_Html::escape(implode('|', [
                $region['country'],
                $region['state'],
                $region['zip'],
                $region['cost'],
            ]));
            $output .= '<tr class="am-grid-row">'
                . sprintf('<td>%s</td>', Am_Di::getInstance()->countryTable->getTitleByCode($region['country']))
                . sprintf('<td>%s</td>', ($region['state'] ? Am_Di::getInstance()->stateTable->getTitleByCode($region['country'], $region['state']) : '*'))
                . sprintf('<td>%s</td>', ($region['zip'] ? $region['zip'] : '*'))
                . sprintf('<td>%s&nbsp;%s</td>', $region['cost'], $currency)
                . sprintf('<td><a href="javascript:" class="shipping_rates-remove local">%s</a>%s</td>',
                        ___('Remove'),
                        "<input type='hidden' name='{$name}[]' value='$hidden'>")
                . '</tr>';
        }

        $output .= '</table></div></div>';
        $id = $this->getId();
        $output .= "
        <style type='text/css'>
            #row-$id .am-element-title { display: none;  }
            #row-$id .am-element { margin-left: 0 }
        </style>
        ";
        return sprintf('<tr class="am-grid-row"><td colspan="2" id="shipping_rates-regions">%s</td></tr>', $output . $this->getJs());
    }

    function getJs()
    {
        $countries = json_encode(Am_Di::getInstance()->countryTable->getOptions());
        $states = [];
        foreach (Am_Di::getInstance()->db->select("SELECT country, state, title FROM ?_state") as $r)
            $states[$r['country']][$r['state']] = $r['title'];
        $states = json_encode($states);
        $name = $this->getName();
        $currency = (string)Am_Currency::getDefault();
        $remove = ___('Remove');
        return <<<CUT
<script type='text/javascript'>
jQuery(function(){
    var countries = $countries;
    var states = $states;
    jQuery("#shipping_rates-country").change(function(){
        var sel = jQuery("#shipping_rates-state");
        sel.find('option').remove();
        var options = states[ jQuery(this).val() ];
        sel.append(jQuery('<option>'));
        if (!options) return;
        jQuery.each(options, function(key, value) {
            sel.append(jQuery('<option>', { value : key })
                .text(value));
        });
    });
    jQuery("#shipping_rates-add-button").click(function(){
        var c = jQuery("#shipping_rates-country").val();
        var r = jQuery("#shipping_rates-cost").val();
        if (!c || !r) {
            flashError("Country and Cost fields are required");
            return;
        }
        var s = jQuery("#shipping_rates-state").val();
        var z = jQuery("#shipping_rates-zip").val();
        //
        var row = jQuery("<tr class='am-grid-row' />");
        row.append(jQuery("<td />").text(countries[c]));
        row.append(jQuery("<td />").text(states[c][s] ? states[c][s] : ''));
        row.append(jQuery("<td />").text(z));
        row.append(jQuery("<td />").text(r + ' $currency'));
        var hidden = jQuery("<input type='hidden' name='{$name}[]'/>").val(c+'|'+s+'|'+z+'|'+r);
        row.append(jQuery("<td><a href='javascript:' class='shipping_rates-remove local'>$remove</a></td>").append(hidden));
        jQuery("#shipping_rates-zip").val("");
        jQuery("#shipping_rates-state").val("");
        jQuery("#shipping_rates-table").append(row);
    });
    jQuery(document).on('click',"a.shipping_rates-remove", function(){
        jQuery(this).closest("tr").remove();
    });
});
</script>
CUT;
    }
}

Am_Di::getInstance()->hook->add(Am_Event::INVOICE_GET_CALCULATORS, function(Am_Event $e) {

if (!class_exists('Am_Invoice_Calc_ShippingFlat', false)) :

class Am_Invoice_Calc_ShippingFlat extends Am_Invoice_Calc
{
    protected $plugin;

    public function __construct(Am_Plugin_ShippingFlat $plugin)
    {
        $this->plugin = $plugin;
    }

    public function calculate(Invoice $invoiceBill)
    {
        //no shipping cost
        if (!$cost = $this->plugin->getCost($invoiceBill->getUser())) return;

        //above free shipping threshold
        if ($this->plugin->getConfig('free_shipping')) {
            $total = 0;
            foreach ($invoiceBill->getItems() as $item) {
                $item->_calculateTotal();
                $total += $item->first_total;
            }

            if ($total >= $this->plugin->getConfig('free_shipping_threshold')) {
                return;
            }
        }

        //calculate shipping fee
        if ($this->plugin->getConfig('per_item_charge')) {
            foreach ($invoiceBill->getItems() as $item) {
                if ($item->is_tangible) {
                    $item->first_shipping = $cost * $item->qty;
                    if ($item->rebill_times > 0) {
                        $item->second_shipping = $cost * $item->qty;
                    }
                }
            }
        } else {
            $tangible_items = [];
            $tangible_second_items = [];
            foreach ($invoiceBill->getItems() as $item) {
                if ($item->is_tangible) {
                    $tangible_items[] = $item;
                    if ($item->rebill_times>0) {
                        $tangible_second_items[] = $item;
                    }
                }
            }

            $cnt = count($tangible_items);
            $cnt_second = count($tangible_second_items);

            if ($cnt) {
                $item = array_shift($tangible_items);
                $item->first_shipping = $cost;
            }

            if ($cnt_second) {
                $item = array_shift($tangible_second_items);
                $item->second_shipping = $cost;
            }
        }

        foreach ($invoiceBill->getItems() as $item) {
            $item->_calculateTotal();
        }
    }
}

endif;

});